﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;

namespace Pinger
{

	public partial class MainForm : Form
	{
		private List<Host> __Hosts = new List<Host>();
		private object _HostsLock = new object();

		public MainForm()
		{
			InitializeComponent();
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			listViewHosts.FullRowSelect = true;
			listViewHosts.HideSelection = false;
			ListViewHelper.EnableDoubleBuffer(listViewHosts);

			//AddHost("localhost");
			//AddHost("127.0.0.1");
			//AddHost("10.0.1.1");
			//AddHost("10.0.1.2");
			//AddHost("10.0.1.240");
			//AddHost("10.0.1.242");
			//AddHost("10.0.1.243");
			//AddHost("10.0.1.249", "CAN");

			ReloadHosts();
		}

		private void ReloadHosts()
		{
			ClearHosts();
			AddHostsFromDB();
			AdjustWidths();
		}

		private void AdjustWidths()
		{
			listViewHosts.BeginUpdate();
			try
			{
				for (int i = 0; i < listViewHosts.Columns.Count; i++)
				{
					listViewHosts.Columns[i].Width = -2;
					int a = listViewHosts.Columns[i].Width;
					listViewHosts.Columns[i].Width = -1;
					int b = listViewHosts.Columns[i].Width;
					int final = Math.Max(100, Math.Max(a, b));
					listViewHosts.Columns[i].Width = final;
				}
			}
			finally
			{
				listViewHosts.EndUpdate();
			}
		}

		private void ClearHosts()
		{
			Cursor.Current = Cursors.WaitCursor;
			try
			{
				Console.WriteLine("-- Starting ClearHosts");
				PingTimer.Stop();
				lock (_HostsLock)
				{
					foreach (var host in __Hosts)
					{
						Console.WriteLine("Cleaning up host: {0}", host);
						var pinger = host.Pinger;
						if (pinger != null)
						{
							pinger.PingCompleted -= new PingCompletedEventHandler(Pinger_PingCompleted);
						}
						else
						{
							Console.WriteLine("No Pinger on host to unbind event!!! {0}", host);
						}
						host.CleanUp();
					}
					__Hosts.Clear();
					listViewHosts.Items.Clear();
				}
				Console.WriteLine("-- Finished ClearHosts");
			}
			finally
			{
				Cursor.Current = Cursors.Default;
			}
		}

		private void AddHostsFromDB()
		{
			Cursor.Current = Cursors.WaitCursor;
			try
			{
				Console.WriteLine("-- Starting AddHostsFromDB");
				var hosts = Hosts.List();
				foreach (var host in hosts)
				{
					AddHost(host);
				}
				PingTimer.Start();
				Console.WriteLine("-- Finished AddHostsFromDB");
			}
			finally
			{
				Cursor.Current = Cursors.Default;
			}
		}

		private void RunPings()
		{
			lock (_HostsLock)
			{
				foreach (var host in __Hosts)
				{
					if (!host.Busy)
					{
						host.Busy = true;
						var pinger = host.Pinger;
						if (pinger != null)
						{
							pinger.SendAsync(host.Name, 5000, host);
						}
						else
						{
							Console.WriteLine("No Pinger on host to Start Async Ping!!! {0}", host);
						}
					}
					else
					{
						Console.WriteLine("Host is still busy: {0}", host);
					}
				}
			}
		}

		public void Pinger_PingCompleted(object sender, PingCompletedEventArgs e)
		{
			var host = e.UserState as Host;
			if (host != null)
			{
				host.Busy = false;
				host.ClearDisplayData();
				if (e.Cancelled)
				{
					host.Status += "Cancelled?";
					host.Color = Color.Gray;
					Console.WriteLine("Ping was cancelled to host: [{0}] !", host.Name);
				}
				if (e.Error != null)
				{
					var innermostMessage = InnermostMessage(e.Error);
					host.Status += string.Format("Error: {0}", innermostMessage);
					host.Color = Color.OrangeRed;
					Console.WriteLine("Error pinging host [{0}]: {1} || {2}", host, e.Error.Message, InnermostMessage(e.Error));
				}
				if (e.Reply != null)
				{
					host.Status += e.Reply.Status.ToString();
					if (e.Reply.Status == IPStatus.Success)
					{
						host.Color = Color.DarkGreen;
						host.LastTimeOK = DateTime.Now;
						host.PingTime = e.Reply.RoundtripTime;
						host.Origin = e.Reply.Address.ToString();
					}
					else
					{
						host.Color = Color.Red;
					}
				}
				UpdateHostDisplay(host);
			}
			else
			{
				Console.WriteLine("Got ping response for unknown host!");
			}
		}

		private string InnermostMessage(Exception exc)
		{
			var innermostException = exc;
			while (innermostException.InnerException != null)
			{
				innermostException = innermostException.InnerException;
			}
			return innermostException.Message;
		}

		private void UpdateHostDisplay(Host host)
		{
			if (this.InvokeRequired)
			{
				MethodInvoker del = delegate
				{
					UpdateHostDisplay(host);
				};
				this.Invoke(del);
				return;
			}

			var item = host.ListViewItem;
			if (item != null)
			{
				item.ListView.BeginUpdate();
				try
				{
					item.SubItems[1].Text = host.Description;
					item.SubItems[2].Text = host.Origin;
					item.SubItems[3].Text = host.Status;
					item.SubItems[4].Text = host.PingTime.ToString();
					item.SubItems[5].Text = FormatLastTimeOKForHumans(host.LastTimeOK);
					//item.ForeColor = host.Color;
					item.BackColor = host.Color;
					//item.SubItems[5].ForeColor = Color.Yellow;
				}
				finally
				{
					item.ListView.EndUpdate();
				}
			}
			else
			{
				Console.WriteLine("No listviewitem on host !!! {0}", host);
			}

		}

		private string FormatLastTimeOKForHumans(DateTime? lastTimeOK)
		{
			if (!lastTimeOK.HasValue)
			{
				return "Never";
			}
			var age = DateTime.Now - lastTimeOK.Value;
			if (age.TotalDays >= 1) { return SinglePlural(age.TotalDays, "Day", "Days"); }
			if (age.TotalHours >= 1) { return SinglePlural(age.TotalHours, "Hour", "Hours"); }
			if (age.TotalMinutes >= 1) { return SinglePlural(age.TotalMinutes, "Minute", "Minutes"); }
			if (age.TotalSeconds >= 1) { return SinglePlural(age.TotalSeconds, "Second", "Seconds"); }
			if (age.TotalMilliseconds >= 1) { return SinglePlural(age.TotalMilliseconds, "Millisecond", "TotalMilliseconds"); }
			return "Just now";
		}

		private string SinglePlural(double value, string single, string plural)
		{
			var tmp = (int)value;
			return string.Format("{0} {1}", tmp, (tmp == 1) ? single : plural);
		}

		private void AddHost(Host host)
		{
			var item = listViewHosts.Items.Add(host.Name);
			host.Init(item);
			host.Pinger.PingCompleted += new PingCompletedEventHandler(Pinger_PingCompleted);
			item.SubItems.Add(host.Description);
			for (int i = 2; i < listViewHosts.Columns.Count; i++)
			{
				//item.SubItems.Add(string.Format("[{0}]", listViewHosts.Columns[i].Text));
				item.SubItems.Add("");
			}
			item.UseItemStyleForSubItems = false;
			lock (_HostsLock)
			{
				__Hosts.Add(host);
			}
		}

		private void PingTimer_Tick(object sender, EventArgs e)
		{
			RunPings();
		}

		private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
		{
			this.Text += " - Closing";
			ClearHosts();
		}

		private void listViewHosts_DoubleClick(object sender, EventArgs e)
		{
			if (listViewHosts.SelectedItems.Count == 1)
			{
				EditSelectedHost();
			}
			else
			{
				AddNewHost();
			}
		}

		private void EditSelectedHost()
		{
			if (listViewHosts.SelectedItems.Count == 1)
			{
				var host = (listViewHosts.SelectedItems[0].Tag as Host);
				using (var form = new HostForm(host))
				{
					if (form.ShowDialog() == DialogResult.OK)
					{
						var ok = Hosts.Update(host);
						ReloadHosts();
					}
				}
			}
		}

		private void DeleteSelectedHosts()
		{
			if (MessageBox.Show("Are you sure you wish to delete the selected host?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
			{
				for (int i = 0; i < listViewHosts.SelectedItems.Count; i++)
				{
					var host = (listViewHosts.SelectedItems[i].Tag as Host);
					var ok = Hosts.Delete(host);
				}
				ReloadHosts();
			}
		}

		private void AddNewHost()
		{
			var host = new Host();
			using (var form = new HostForm(host))
			{
				if (form.ShowDialog() == DialogResult.OK)
				{
					var ok = Hosts.Insert(host);
					ReloadHosts();
				}
			}
		}

		private void MainForm_KeyUp(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Oemplus:
				case Keys.Insert:
					AddNewHost();
					break;
				case Keys.Enter:
					if (e.Shift)
					{
						EditSelectedHost();
					}
					break;
				case Keys.Delete:
					DeleteSelectedHosts();
					break;
				case Keys.F5:
					ReloadHosts();
					break;
				case Keys.Multiply:
					AdjustWidths();
					break;
				default:
					break;
			}
		}


	}
}
